﻿using EasyCore.App;
using EasyCore.Extensions;
using EasyCore.UnifyResult.Exceptions;
using IP2Region.Net.Abstractions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Shyjus.BrowserDetection;
using SqlSugar;
using System.ComponentModel;
using System.Diagnostics;

namespace EasyCore.EndpointAudits
{
    /// <summary>
    /// API过淲器
    /// </summary>
    public class EndpointAuditFilter : IAsyncActionFilter
    {
        private readonly IBrowserDetector _browserDetector; //浏览器对象
        private readonly ISearcher _searcher; //IP地址转换器
        private readonly EndpointAuditOption _option;//选项值

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="browserDetector"></param>
        /// <param name="searcher"></param>
        public EndpointAuditFilter(IOptions<EndpointAuditOption> options, IBrowserDetector browserDetector, ISearcher searcher)
        {
            _option = options.Value;
            _browserDetector = browserDetector;
            _searcher = searcher;
        }

        /// <summary>
        /// 执行方法
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            string exceptionMsg = string.Empty; //异常消息
            RepeatSubmitLimitExpireType repeat = RepeatSubmitLimitExpireType.AutoExpire;
            string repeatKey = string.Empty;
            ObjectResult result = null;
            var sw = new Stopwatch(); //用于请求的开始时间
            sw.Start();//开始
            try
            {
                if (!_option.DisableRepeatSubmitValidation)
                {
                    (repeat, repeatKey) = RepeatSubmit(context);// 防重复提交
                }
                var resultContext = await next(); //执行方法委托
                if (resultContext.Result is ObjectResult)
                {
                    result = resultContext.Result as ObjectResult;
                }
            }
            catch (Exception ex)
            {
                exceptionMsg = string.Format("请求异常：{0}", ex.Message);
            }
            finally
            {
                sw.Stop();//结束
                if (!_option.DisableAuditLogSave)
                {
                    var action = (ControllerActionDescriptor)context.ActionDescriptor;//接口描述器
                    if (!action.MethodInfo.IsDefined(typeof(IgnoreEndpointAuditAttribute), false))
                    {
                        var auditLog = await GetAuditLog(context);
                        auditLog.ExecutionDuration = sw.ElapsedMilliseconds;
                        if (!string.IsNullOrWhiteSpace(exceptionMsg)) auditLog.ResponseData = exceptionMsg;
                        else if (result != null)
                        {
                            var body = result?.Value.ToJson();
                            auditLog.ResponseData = body;
                        }
                        //获得审计日志仓储
                        var scope = EasyApp.Root.CreateScope();
                        var handler = scope.ServiceProvider.GetRequiredService<IEndpointAuditHandler>();
                        if (handler != null)
                        {
                            await handler.HandleAsync(auditLog);
                        }
                    } //接口忽略审计日志
                }
                //清空重复提交记录
                if (!_option.DisableRepeatSubmitValidation)
                {
                    if (repeat == RepeatSubmitLimitExpireType.AutoExpire)
                    {
                        EasyApp.Cache.Remove(repeatKey);
                    }
                }
            }
        }

        /// <summary>
        /// 防重复提交
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <exception cref="AlertException"></exception>
        private (RepeatSubmitLimitExpireType, string) RepeatSubmit(ActionExecutingContext context)
        {
            var repeat = context.ActionDescriptor.EndpointMetadata.OfType<RepeatSubmitLimitAttribute>().FirstOrDefault();
            if (repeat == null) return (RepeatSubmitLimitExpireType.None, null);

            var request = context.HttpContext.Request;
            // 获取用户标识，这里假设使用身份验证后的用户 ID，你可以根据实际情况修改
            // 生成包含用户标识的请求唯一标识
            var key = $"{EasyApp.UserId}:{request.Method}:{request.Path}";
            if (EasyApp.Cache.ContainsKey(key)) BusinessThrow.Throw("重复提交，请稍后再试");
            if (repeat.Expire.HasValue && repeat.Expire.Value > 0)
            {
                EasyApp.Cache.Add(key, repeat.Expire.Value);
                return (RepeatSubmitLimitExpireType.FixedExpire, key);
            }
            return (RepeatSubmitLimitExpireType.AutoExpire, key);
        }



        /// <summary>
        /// 获得审计日志基本信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task<EndpointAuditResult> GetAuditLog(ActionExecutingContext context)
        {
            var httpContext = context.HttpContext;
            var remoteIp = httpContext.GetIP();
            var args = await httpContext.GetAllRequestParams();
            var route = context.ActionDescriptor.RouteValues;

            var desc = ((ControllerActionDescriptor)context.ActionDescriptor).MethodInfo.GetCustomAttributes(typeof(DescriptionAttribute), true).OfType<DescriptionAttribute>().FirstOrDefault();

            var auditLog = new EndpointAuditResult
            {
                Controller = httpContext.GetControllerName(),
                Action = httpContext.GetActionName(),
                Method = httpContext.Request.Method,
                Description = desc?.Description,
                RequestUrl = httpContext.Request.Path,
                RequestParameters = args.ToJson(),
                RequestIp = remoteIp,
                IpAddress = _searcher.Search(remoteIp),
                OperatingSystem = _browserDetector.Browser?.OS,
                DeviceType = _browserDetector.Browser?.DeviceType,
                BrowserName = _browserDetector.Browser?.Name,
                Version = _browserDetector.Browser?.Version,
            };
            return auditLog;

        }
    }
}
